﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using YetAnotherChat.TcpConsole.Model;
using YetAnotherChat.TcpServer;
using YetAnotherChat.UserManagement.Models;

namespace YetAnotherChat.TcpConsole
{
    /// <summary>
    /// Managing connections comes in and out, managing online users list, name changes
    /// </summary>
    internal class OnlineSessionManager
    {
        /// <summary>
        /// Store the list of online users. 
        /// </summary>
        public static ConcurrentBag<OnlineUser> OnlineUsers = new ConcurrentBag<OnlineUser>();

        public OnlineSessionManager(WebSocketServer socketServer)
        {
            socketServer.ClientConnected += new EventHandler<ConnectionEventArgs>(aServer_ClientConnected);
            socketServer.ClientConnecting += new EventHandler<ConnectionEventArgs>(aServer_ClientConnecting);
            socketServer.ClientDisconnecting += new EventHandler<ConnectionEventArgs>(aServer_ClientDisconnecting);

        }

        void aServer_ClientDisconnecting(object sender, ConnectionEventArgs e)
        {
            Console.WriteLine("Client Disconnected : " + e.Connection.ClientAddress);
            var user = OnlineUsers.Where(o => o.ConnectionId == e.Connection.ConnectionId).FirstOrDefault();

            if (user != null)
            {
                OnlineUser disconnectinUser;

                if (OnlineUsers.TryTake(out disconnectinUser) && disconnectinUser != null)
                {
                    //temporary fix when user object is not implemented
                    if (user.User == null)
                        user.User = new ChatUser() { DisplayName = "unknown" };

                    var r = new YetAnotherChat.TcpConsole.Response { Type = ResponseType.Disconnect, Data = new { user.User.DisplayName } };

                    e.Connection.Server.Broadcast(e.Connection.ConnectionId, JsonConvert.SerializeObject(r));
                }

                BroadcastNameList(e.Connection.ConnectionId, e.Connection.Server);
            }
        }

        void aServer_ClientConnecting(object sender, ConnectionEventArgs e)
        {
            Debug.WriteLine("Client connecting From : " + e.Connection.ClientAddress);
        }

        void aServer_ClientConnected(object sender, ConnectionEventArgs e)
        {
            Debug.WriteLine("Client Connection From : " + e.Connection.ClientAddress);

            var me = new OnlineUser { ConnectionId = e.Connection.ConnectionId };

            OnlineUsers.Add(me);
        }

        /// <summary>
        /// Broadcasts a list of all online users to all online users
        /// </summary>
        private void BroadcastNameList(Guid fromConnection, WebSocketServer server)
        {
            var r = new Response
            {
                Type = ResponseType.UserCount,
                Data = new { Users = OnlineUsers.ToArray() }
            };
            server.Broadcast(fromConnection, JsonConvert.SerializeObject(r));
        }

        /// <summary>
        /// Update a user's name if they sent a name-change command from the client.
        /// </summary>
        /// <param name="name">The name to be changed to</param>
        /// <param name="aContext">The user's connection context</param>
        public void NameChange(string name, ServerConnection connection)
        {
            var u = OnlineUsers.Where(o => o.ConnectionId == connection.ConnectionId).Single();

            if (ValidateName(name))
            {
                var r = new Response
                {
                    Type = ResponseType.NameChange,
                    Data = new { Message = u.User.DisplayName + " is now known as " + name }
                };
                connection.Server.Broadcast(connection.ConnectionId, JsonConvert.SerializeObject(r));

                u.User.DisplayName = name;

                BroadcastNameList(connection.ConnectionId, connection.Server);
            }
            else
            {
                MessageBroadcaster.SendError("Name is of incorrect length.", connection);
            }
        }

        /// <summary>
        /// Checks validity of a user's name
        /// </summary>
        /// <param name="name">Name to check</param>
        /// <returns></returns>
        private bool ValidateName(string name)
        {
            var isValid = false;
            if (name.Length > 3 && name.Length < 25)
            {
                isValid = true;
            }

            return isValid;
        }

        public OnlineUser GetUser(Connection connection)
        {
            return OnlineUsers.Where(o => o.ConnectionId == connection.ConnectionId).Single(); ;
        }

        /// <summary>
        /// Register a user's context for the first time with a username, and add it to the list of online users
        /// </summary>
        /// <param name="name">The name to register the user under</param>
        /// <param name="context">The user's connection context</param>
        public void Register(string name, ServerConnection connection)
        {
            var u = GetUser(connection);

            //use cookie or something to get chat user object from
            //ChatUI project. For now just create a new ChatUser object
            u.User = new ChatUser();


            var r = new Response();

            if (ValidateName(name))
            {
                u.User.DisplayName = name;

                r.Type = ResponseType.Connection;
                r.Data = new { name };

                connection.Server.Broadcast(connection.ConnectionId, JsonConvert.SerializeObject(r));

                BroadcastNameList(connection.ConnectionId, connection.Server);
            }
            else
            {
                MessageBroadcaster.SendError("Name is of incorrect length.", connection);
            }
        }
    }
}
